/**
 *
 * jcrt and its source-code is licensed under the LGPL.
 * You may copy, adapt, and redistribute this file for commercial or non-commercial use.
 * When copying, adapting, or redistributing this document in keeping with the guidelines above,
 * you are required to provide proper attribution to obinary.
 * If you reproduce or distribute the document without making any substantive modifications to its content,
 * please use the following attribution line:
 *
 * Copyright 2007 Atjava.com Ltd. (http://www.atjava.com) All rights reserved.
 *
 */
package com.atjava.jcrt.repository.jackrabbit;

import java.io.File;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.jcr.NamespaceException;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.Workspace;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.xml.transform.TransformerFactoryConfigurationError;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.jackrabbit.core.WorkspaceImpl;
import org.apache.jackrabbit.core.jndi.RegistryHelper;
import org.apache.log4j.Logger;

import com.atjava.jcrt.core.SystemProperty;
import com.atjava.jcrt.core.transaction.XmlJcrtInfo;
import com.atjava.jcrt.repository.Provider;
import com.atjava.jcrt.repository.RepositoryNotInitializedException;
/**
 * 
 * @author biggie
 * @version $Revision: 1.2 $ $Date: 2008/07/23 07:18:38 $
 */
public class ProviderImpl implements Provider {
	
	private static Logger log = Logger.getLogger (ProviderImpl.class.getName()) ;
	
	private Repository repository;
	
	private XmlJcrtInfo xmlJcrtInfo = null;

	public Repository getUnderlineRepository() throws RepositoryNotInitializedException {
        if (this.repository == null) {
            throw new RepositoryNotInitializedException("Null repository");
        }
        return this.repository;
	}

    /**
     * Find the physical path to the repository folder
     * @param repositoryHome the property set in the repository.xml file
     * @return the full path resolved to the repository dir
     */
    private String getRepositoryHome(final String repositoryHome) {
        if (repositoryHome.startsWith("/") || repositoryHome.substring(1,2).equals(":")) {
            return repositoryHome;
        }
        return SystemProperty.SYSTEM_REALPATH + File.separator+ repositoryHome ;
    }
    /**
     * Find the physical path to the configfile folder
     * @param configFile jcr.cfg.xml file
     * @return the full path resolved to the repository dir
     */
    private String getConfigFile(final String configFile){
        if (configFile.startsWith("/") || configFile.substring(1,2).equals(":")) {
            return configFile;
        }
        return SystemProperty.SYSTEM_REALPATH + File.separator+ configFile ;    	
    }
	public void init(XmlJcrtInfo xmlJcrtInfo) throws RepositoryNotInitializedException {
		checkXmlSettings();
		
		this.xmlJcrtInfo = xmlJcrtInfo;
		final Hashtable<String, String> env = new Hashtable<String, String>();
		Map params = this.xmlJcrtInfo.getRepositoryparams();
		
		String configFile = (String)params.get(SystemProperty.JCRT_CONFIG_FILE);
		configFile = getConfigFile(configFile);
		String repositoryHome = (String) params.get(SystemProperty.JCRT_REPOSITORY_HOME);
		repositoryHome = getRepositoryHome(repositoryHome);
		String contextFactoryClass = (String) params.get(SystemProperty.JCRT_CONTEXT_FACTORY_CLASS);
		String providerURL = (String) params.get(SystemProperty.JCRT_PROVIDER_URL);
		String bindName = (String) params.get(SystemProperty.JCRT_BIND_NAME);
		
		env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactoryClass);
        env.put(Context.PROVIDER_URL, providerURL);
        try {
            InitialContext ctx = new InitialContext(env);
            try {
                this.repository = (Repository) ctx.lookup(bindName);
            }
            catch (NameNotFoundException ne) {
                log.error(
                        "No JNDI bound Repository found with name {} , trying to initialize a new Repository "+bindName,
                        ne);
                RegistryHelper.registerRepository(ctx, bindName, configFile, repositoryHome, true);
                this.repository = (Repository) ctx.lookup(bindName);
            }
            this.validateWorkspaces();
        }
        catch (NamingException e) {
            log.error("Unable to initialize repository: " + e.getMessage(), e);
            throw new RepositoryNotInitializedException(e);
        }
        catch (RepositoryException e) {
            log.error("Unable to initialize repository: " + e.getMessage(), e);
            throw new RepositoryNotInitializedException(e);
        }
        catch (TransformerFactoryConfigurationError e) {
            log.error("Unable to initialize repository: " + e.getMessage(), e);
            throw new RepositoryNotInitializedException(e);
        }
	}
	/**
	 * Return JCR Session
	 * @param workspaceName
	 * @return
	 * @throws RepositoryException
	 */
	public Session getSession(String workspaceName) throws RepositoryException{
        SimpleCredentials credentials = new SimpleCredentials(
                this.xmlJcrtInfo.getRepusername(),
                this.xmlJcrtInfo.getReppassword().toCharArray());
        String wkname = workspaceName;
        if (wkname!=null && workspaceName.equals("default")) wkname = null; 
        
        return this.repository.login(credentials, wkname);
	}
    /**
     * create all workspaces
     */
    private void validateWorkspaces() throws RepositoryException {
        Iterator configuredNames = this.xmlJcrtInfo.getWorkspaces().iterator();
        while (configuredNames.hasNext()) {
        	String workspacename = (String) configuredNames.next();
        	String wkname = null;
        	if (!workspacename.equals("default"))  wkname = null;
            if(registerWorkspace(wkname)){
                // check if workspace already exists
                SimpleCredentials credentials = new SimpleCredentials(
                    this.xmlJcrtInfo.getRepusername(),
                    this.xmlJcrtInfo.getReppassword().toCharArray());
                Workspace workspace = this.repository.login(credentials,wkname).getWorkspace();
                List npsList = (List)this.xmlJcrtInfo.getNamespaces().get(workspacename);
                for(int i=0;i<npsList.size();i++){
                	String[] nps = (String[])npsList.get(i);
                	registerNamespace(nps[0],nps[1],workspace);
                }
            }
        }
    }
	public void registerNamespace(String prefix, String uri, Workspace workspace) throws RepositoryException {
        try {
            workspace.getNamespaceRegistry().getURI(prefix);
        }
        catch (NamespaceException e) {
            if (log.isDebugEnabled()) {
                log.debug(e.getMessage());
            }
            log.info("registering prefix [{"+prefix+"}] with uri {"+uri+"}"); //$NON-NLS-1$
            workspace.getNamespaceRegistry().registerNamespace(prefix, uri);
        }
	}

	public boolean registerWorkspace(String workspaceName) throws RepositoryException {
		if (workspaceName == null ) return true; 
        // check if workspace already exists
        SimpleCredentials credentials = new SimpleCredentials(
            this.xmlJcrtInfo.getRepusername(),
            this.xmlJcrtInfo.getReppassword().toCharArray());
        Session jcrSession = this.repository.login(credentials);
        try {
            WorkspaceImpl defaultWorkspace = (WorkspaceImpl) jcrSession.getWorkspace();
            String[] workspaceNames = defaultWorkspace.getAccessibleWorkspaceNames();

            boolean alreadyExists = ArrayUtils.contains(workspaceNames, workspaceName);
            if (!alreadyExists) {
                defaultWorkspace.createWorkspace(workspaceName);
            }
            jcrSession.logout();
            return !alreadyExists;
        } catch (ClassCastException e) {
            // this could happen if the repository provider does not have proper Shared API for the
            // application server like at the moment in Jackrabbit
            log.debug("Unable to register workspace, will continue", e);
        } catch (Throwable t) {
            log.error("Unable to register workspace, will continue", t);
        }
        return false;
	}

	public void unregisterNamespace(String prefix, Workspace workspace) throws RepositoryException {
		workspace.getNamespaceRegistry().unregisterNamespace(prefix);
	}
    /**
     * WORKAROUND for tomcat 5.0/jdk 1.5 problem tomcat\common\endorsed contains an xml-apis.jar needed by tomcat and
     * loaded before all xmsl stuff present in the jdk (1.4 naming problem). In the xml-apis.jar file the
     * TransformerFactoryImpl is set to "org.apache.xalan.processor.TransformerFactoryImpl" instead of
     * "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl". solution: remove the file xml-apis.jar
     * from the directory OR manually change the javax.xml.transform.TransformerFactory system property
     */
    protected void checkXmlSettings() {
        if (SystemUtils.isJavaVersionAtLeast(1.5f)
            && "org.apache.xalan.processor.TransformerFactoryImpl".equals(System
                .getProperty("javax.xml.transform.TransformerFactory"))) {

            log.info("Java 1.5 detected, setting system property \"javax.xml.transform.TransformerFactory\" to "
                + "\"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl\"");

            System.setProperty(
                "javax.xml.transform.TransformerFactory",
                "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
        }
    }
}
